#include "CargarDatos.h"

using namespace std;

int cant=0;
double error= 0.000000001;
vector<double> autovalores;
//vector<double> autovaloresViejos;


int cargarParam(FILE* datos)
{
    char buffer[1024]=" ";
    char temp = getc(datos);
    int i = 0;
    while(!(temp == ' ' || temp == '\n'))
    {
        buffer[i] = temp;
        temp = getc(datos);
        i++;
    }
    return atoi(buffer);
}


Matriz crearMatrizK(int n,int* coeficientes)
{
    Matriz K(n,n);

    K.set(1,1,(double)(- coeficientes[1] - coeficientes[2]));
    K.set(1,2,(double)(coeficientes[2]));

    for(int i=2; i<n; i++)
    {
        K.set(i,i, (double)(-coeficientes[i] - coeficientes[i+1]));
        K.set(i,i+1, (double)(coeficientes[i+1]));
        K.set(i,i-1, (double)(coeficientes[i]));
    }

    K.set(n,n,(double)(-coeficientes[n]));
    K.set(n,n-1,(double)(coeficientes[n]));

    return K;
}


Matriz crearMatrizM(int n, int m0, int ml, int mp, int* livianos, int* pesados)
{
    Matriz M(n,n);

    for(int i=1; i<=n; i++)     M.set(i,i, (double)(m0 + livianos[i]*ml + pesados[i]*mp));

    return M;
}


void haceLaMagia(int n,int m0,int ml,int mp,int* pesados,int* livianos,int* coeficientes)
{
    Matriz K =  crearMatrizK(n,coeficientes);
    cout<<"Creando la Matriz M"<<endl;
    Matriz M = crearMatrizM(n,m0,ml,mp, livianos, pesados);

    cout<<"Creando la Matriz M-1"<<endl;
    M.invertir();

    cout<<"Creando la Matriz A = M-1 * K"<<endl;

    Matriz A = M.multiplicar(K);

    cout<<"Arrancando Algoritmo QR"<<endl;
    int i =0;
    double promedio = 1;


    while(fabs(promedio)>error)
    {
        i++;
        promedio = A.get(1,1);
        Matriz R = A;
        Matriz Q = calcularQRRotaciones(R);
        Q.redondear();
        R.redondear();

        A = R.multiplicar(Q);

        promedio -= A.get(1,1);
    }

    cout<<endl<< "Se realizaron "<<i<<" iteraciones"<<endl<<endl;
    cout<<endl<<"Los Coeficientes de rigides: "<<endl;

    autovalores.resize(n+1);
///Imprimir Autovalores
    for(int i=1; i<=A.size(); i++)
    {
        if(A.get(i,i)>0)
        {
            cout<<endl<<endl<<"ERROR!!!! El AUTOVALOR "<<A.get(i,i)<<" DEBERIA SER NEGATIVO!!"<<endl<<endl;
            exit(-1);
        }
        autovalores[i]= sqrt(-A.get(i,i));
        cout<<i<<"- "<<autovalores[i]<<endl;
    }

    ///Imprimir Autos livianos y pesados
    cout<<endl;
    bool pisosSeguros[n+1];

    estaSeguro(n, autovalores,pisosSeguros);
    cout<<"Pisos Seguros: "<< endl;
    for(int i=1; i<=n; i++) cout<<setw(2)<<pisosSeguros[i]<<" ";
    cout<<endl;

    if(!estaSeguro(n, autovalores,pisosSeguros))
    {
        cout<<"Cant de movimientos: "<<cant++<<endl;
        reacomodar4(n,livianos,pesados,pisosSeguros);                                               ///HEURISTICA
        cout<<"Livianos: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(2)<<livianos[i]<<" ";
        cout<<endl<<endl;
        cout<<"Pesados: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(2)<<pesados[i]<<" ";
        cout<<endl<<endl;

     //   pressKey();
        haceLaMagia(n, m0, ml, mp, pesados, livianos, coeficientes);
    }
    else
    {
        cout<<endl<<endl<<"EL EDIFICIO ESTA SEGURO"<<endl;
        cout<<"Livianos: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<livianos[i]<<" ";
        cout<<endl<<endl;
        cout<<"Pesados: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<pesados[i]<<" ";
        cout<<endl;
        cout<<"Cant de movimientos: "<<cant<<endl<<endl;
    }
}


bool estaSeguro(int n, vector<double> autovalores, bool* pisosSeguros)
{
    bool res = true;
    for(int i=1; i<=n; i++)
    {
        pisosSeguros[i] = fabs(autovalores[i]-3) > 0.3;
        res = res && pisosSeguros[i];
    }
    cout<<"Edificio seguro?: "<< res<<endl;
    return res;
}


void reacomodar1(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Desalojo 1L y 1P a cada otro piso que este seguro
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && pesados[i]>0 && livianos[i]>0)
                {
                    livianos[i]--;
                    livianos[j]++;
                    pesados[i]--;
                    pesados[j]++;
                    return;
                }
                if(pisosSeguros[j] && pesados[i]>0)
                {
                    pesados[i]--;
                    pesados[j]++;
                    return;
                }
                if(pisosSeguros[j] && livianos[i]>0)
                {
                    pesados[i]--;
                    pesados[j]++;
                    return;
                }
            }
        }
    }
    cout<<endl<<"CON ESTE METODO NO HAY SOLUCION!!"<<endl<<endl;
    exit(-1);
}

void reacomodar2(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Desalojo primero los pesados y despues los livianos
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && (pesados[i]>0 || livianos[i]>0))
                {
                    if(pesados[i]==0)
                    {
                        livianos[i]--;
                        livianos[j]++;
                    }
                    else
                    {
                        pesados[i]--;
                        pesados[j]++;
                    }
                    return;
                }
            }
        }
    }
    cout<<endl<<"CON ESTE METODO NO HAY SOLUCION!!"<<endl<<endl;
    exit(-1);
}

void reacomodar3(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Agrego al piso primero los pesados y despues los livianos
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && (pesados[j]>0 || livianos[j]>0))
                {

                    if(pesados[j]==0)
                    {
                        livianos[j]--;
                        livianos[i]++;
                    }
                    else
                    {
                        pesados[j]--;
                        pesados[i]++;
                    }
                    return;
                }
            }
        }
    }
    cout<<endl<<"CON ESTE METODO NO HAY SOLUCION!!"<<endl<<endl;
    exit(-1);
}

void reacomodar5(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Acomodo segun los coeficientes de rigidez
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            if(autovalores[i]< 3)
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3 && autovalores[j]<3.3 && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0 && i!=j)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return;
                    }

                    if(autovalores[j]>3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return;
                    }

                    if(autovalores[j]<2.3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return;
                    }
                }
                    cout<<endl<<"CON ESTE METODO NO HAY SOLUCION!!"<<endl<<endl;exit(-1);
            }
            else
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<3 && autovalores[j]>2.7 && i!=j && (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[i]--;
                            livianos[j]++;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[i]--;
                            pesados[j]++;
                        }
                        return;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    //cout<<"ASDASDASDAS "<<j<<" "<<(autovalores[j]<3)<<endl;
                    if(autovalores[j]<3 && i!=j && (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3.3 && i!=j && (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return;
                    }
                }
                cout<<endl<<"CON ESTE METODO NO HAY SOLUCION!!"<<endl<<endl;exit(-1);
            }
        }

    }
}





void reacomodar6(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Mando 1L y 1P a cada otro piso que este seguro
{
    int k=1;
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            if(autovalores[i]< 3)
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>autovalores[k] && j!=i)
                    {
                        k=j;
                    }
                }
                livianos[i]+=livianos[k];
                pesados[i]+=pesados[k];
                livianos[k]= 0;
                pesados[k] = 0;
            }
            else
            {
                for(int j=1; j<=n; j++)
                {
                    for(int j=1; j<=n; j++)
                    {
                        if(autovalores[j]>autovalores[k] && j!=i)
                        {
                            k=j;
                        }
                    }
                    livianos[k]+=livianos[i];
                    pesados[k]+=pesados[i];
                    livianos[i]=0;
                    pesados[i]=0;
                }
            }
        }
    }
}


void haceTodaLaMagia(char* archivo)
{

    FILE* datos = fopen(archivo, "r");

    int n = cargarParam(datos);
    int m0 = cargarParam(datos);
    int ml = cargarParam(datos);
    int mp = cargarParam(datos);
    int pesados[n+1], livianos[n+1], coeficientes[n+1];

    for(int i=1; i<=n; i++)        coeficientes[i] = cargarParam(datos);
    for(int i=1; i<=n; i++)        livianos[i] = cargarParam(datos);
    for(int i=1; i<=n; i++)        pesados[i] = cargarParam(datos);

    haceLaMagia(n, m0, ml, mp, pesados, livianos, coeficientes);

}


void pressKey()
{
    //the struct termios stores all kinds of flags which can manipulate the I/O Interface
    //I have an old one to save the old settings and a new
    static struct termios oldt, newt;


    //tcgetattr gets the parameters of the current terminal
    //STDIN_FILENO will tell tcgetattr that it should write the settings
    // of stdin to oldt
    tcgetattr( STDIN_FILENO, &oldt);
    //now the settings will be copied
    newt = oldt;

    //two of the c_lflag will be turned off
    //ECHO which is responsible for displaying the input of the user in the terminal
    //ICANON is the essential one! Normally this takes care that one line at a time will be processed
    //that means it will return if it sees a "\n" or an EOF or an EOL
    newt.c_lflag &= ~(ICANON | ECHO );

    //Those new settings will be set to STDIN
    //TCSANOW tells tcsetattr to change attributes immediately.
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    //now the char wil be requested
    getchar();

    //the old settings will be written back to STDIN
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}
